/**
 * @file keyled.c
 * @brief 按键与LED驱动实现
 * @author 废话文学创始人
 * @version 1.0
 * @date 2024-02-25
 */

#include "keyled.h"

KEY KEYS[KEYNUMMER] = {0};
uint8_t keyused = 0;

uint32_t KeyFlag = 0x0000;
uint32_t LastKeysValue = 0, CurKeysValue = 0;

/**
 * @brief 按键注册
 * @param GPIOx 按键对应端口
 * @param GPIO_Pin 按键对应管脚
 * @param KeyMask 按键掩码，详见keyled.h
 * @param KeyAction 按键单击的回调函数
 * @return 注册结果，0为失败，1为成功
 */
uint8_t Key_Register(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint32_t KeyMask, void (*KeyAction)(void))
{
#ifdef USE_FULL_ASSERT
    assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
    assert_param(IS_GPIO_PIN(GPIO_Pin));
    assert_param(IS_KEYMASK(KeyMask));
#else
    if (!IS_GPIO_ALL_INSTANCE(GPIOx))
        return 0;
    if (!IS_GPIO_PIN(GPIO_Pin))
        return 0;
    if (!IS_KEYMASK(KeyMask))
        return 0;
#endif
    if (keyused >= KEYNUMMER)
        return 0;
    if (KeyAction == NULL)
        return 0;
    KEYS[keyused].GPIOx = GPIOx;
    KEYS[keyused].GPIO_Pin = GPIO_Pin;
    KEYS[keyused].KeyMask = KeyMask;
    KEYS[keyused].KeyAction = KeyAction;
    keyused++;
    return 1;
}

/**
 * @brief 阻塞读取某一按键值
 * @param key 按键结构体
 */
void ReadOneKey_block(KEY key)
{
    if (HAL_GPIO_ReadPin(key.GPIOx, key.GPIO_Pin) == KEY_ON)
    {
        delay_ms(20);
        if (HAL_GPIO_ReadPin(key.GPIOx, key.GPIO_Pin) == KEY_ON)
        {
            KeyFlag = KeyFlag | (key.KeyMask);
            while (HAL_GPIO_ReadPin(key.GPIOx, key.GPIO_Pin) == KEY_ON)
                ;
        }
    }
}

/**
 * @brief 阻塞读取所有按键值
 * @note 标志存入KeyFlag
 */
void Key_Read_block(void)
{
    for (int i = 0; i < keyused; i++)
    {
        ReadOneKey_block(KEYS[i]);
    }
}

/**
 * @brief 非阻塞读取某一按键值
 * @param key 按键结构体
 */
static void ReadOneKey_unblock(KEY key)
{
    if (HAL_GPIO_ReadPin(key.GPIOx, key.GPIO_Pin) == KEY_ON)
        CurKeysValue = CurKeysValue | (key.KeyMask);
    else
        CurKeysValue = CurKeysValue & (~key.KeyMask);
    if (((CurKeysValue & key.KeyMask) == 0) && ((LastKeysValue & key.KeyMask) != 0))
        KeyFlag = KeyFlag | (key.KeyMask);
}

/**
 * @brief 非阻塞式按键扫描
 * @param division 控制函数的实际执行次数
 * @note 标志存入KeyFlag
 * @note 无滤波算法，当实际扫描间隔大于20ms即可去抖
 * @note 建议扫描时间在20ms~100ms内
 */
void Key_Read_unblock(uint32_t division)
{
    static uint32_t count = 0;
    count++;
    if (count >= division)
    {
        count = 0;
        LastKeysValue = CurKeysValue;
        for (int i = 0; i < keyused; i++)
        {
            ReadOneKey_unblock(KEYS[i]);
        }
    }
}

/**
 * @brief 读取按键值并清零
 * @return 当前按键标志位
 */
uint32_t Key_Flag_Receive(void)
{
    uint32_t temp;
    temp = KeyFlag;
    KeyFlag = 0x0000;
    return temp;
}

/**
 * @brief 不同按键的回调函数
 * @param KeyCallBack 某个按键对应的回调函数
 */
static void SomeKeyAction(void (*KeyCallBack)(void))
{
    KeyCallBack();
}

/**
 * @brief 按键单击处理，使用回调函数
 * @param KeyFlag 按键标志位
 */
void Key_Flag_Action(uint32_t KeyFlag)
{
    for (int i = 0; i < keyused; i++)
    {
        if (KeyFlag & KEYS[i].KeyMask)
        {
            SomeKeyAction(KEYS[i].KeyAction);
        }
    }
}